#include "CSfx.h"
//#include "../CGame.h"

#include "Application.h"

CSfx::CSfx():
	m_filename(string()),
	m_lastUpdateTime(0),
	m_status(kInvalid),
	m_track(0),
	m_mpcStream(0),
	m_isStatic(false),
	m_nLoop(0),
	m_nStaticSample(0)
{
	for(int i = 0; i < CTrackStereo::NUM_BUFFER; i++)
	{
		m_buffer[i] = 0;
	}
}

////////////////////////////////////////////////////////////////////////
CSfx::~CSfx()
{
	CleanUp();
}

void CSfx::Init(string filename, bool isStatic)
{
	if(m_filename.length() < 1)
	{
		Stop();
	}
	m_isStatic = isStatic;

	m_filename = filename;
	if(isStatic && !m_track)
		InitStatic();

	m_status = kInvalid;
}

void CSfx::ReadWholeBuffer()
{
	if(m_filename.length() < 1 || m_isStatic == true)
		return;

	InitStatic();
}

void CSfx::EmptyBuffer()
{
	if(!m_track)
		return;

	m_isStatic = false;
	m_track->Stop();
	CleanUp();
}

void CSfx::CleanUp()
{
	SAFE_DEL(m_track);
	SAFE_DEL(m_mpcStream);
	for(int i = 0; i < CTrackStereo::NUM_BUFFER; i++)
	{
		SAFE_DEL(m_buffer[i]);
	}
	m_status = kInvalid;
	m_isStatic = false;
}

void CSfx::Play(int loop, int period)
{
	m_period = period;
	m_nLoop = loop - 1;
	m_lastUpdateTime = Application::instance()->GetGameTime();

	if(m_isStatic)
		PlayStatic();
	else
		PlayStream();
}

CTrackStereo*
CSfx::getTrack()
{
	// make sure we have a filled buffer
	if(!m_track)
	{ InitStatic(); }

	// spawn off an independent static track
	CTrackStereo *track	= NEW CTrackStereo();
	track->Init( m_channel, m_rate, m_bufferSize);
	track->Update(0, m_buffer[0], 0, m_nStaticSample * m_channel * 2);

	return track;
}

void CSfx::Stop()
{
	if(!m_track)
		return;

	m_track->Stop();

	if(!m_isStatic)
		CleanUp();
}

void CSfx::Pause()
{
	if(m_track)
		m_track->Pause();
}

void CSfx::Unpause()
{
	if(m_track)
		m_track->Play();
}

void CSfx::Restart()
{
	if(!m_track)
		return;

	if(m_isStatic)
		RestartStatic();
	else
		RestartStream();
}

void CSfx::Update()
{
	if(!m_track)
		return;

	if(m_status == kPlaying)
	{
		int status = m_track->GetSourceState();
		if(status != CTrackStereo::k_waveStatus_playing)
		{
			if(m_nLoop > 0 || m_nLoop < -1)
			{
				if(m_nLoop > 0){ --m_nLoop; }
				m_status = kLooping;
			}
			else
			{
				if(!m_isStatic)
					Stop();
				m_status = kInvalid;
				return;
			}
		}
	}

	if(m_status == kLooping)
	{
		if(m_period < (Application::instance()->GetGameTime() + m_lastUpdateTime))
		{
			Restart();
		}
		else
		{
			return;
		}
	}

	if(m_track->GetProcessedBuffer() > 0 && !m_isStatic)
	{
		int nSample = m_mpcStream->Decode((short*)m_buffer[m_currentBuffer], m_bufferSize/m_channel/2);
		if(nSample)
		{
			m_track->Update(Application::instance()->GetGameTime() - m_lastUpdateTime, (audio::s8*)(m_buffer[m_currentBuffer]), -1, nSample*m_channel*2);
			m_currentBuffer = (++m_currentBuffer)%(CTrackStereo::NUM_BUFFER);
			m_status = kPlaying;
		}
		//else
		//{
		//	m_status = kInvalid;
		//}
	}
}

void CSfx::SetVolumeGain(float gain)
{
	if(m_track)
		m_track->SetVolumeGain(gain);
}

void CSfx::SetVolume(int volume)
{
	if(m_track)
		m_track->SetVolume(volume);
}

bool CSfx::ReInitSoundInstance(void)
{
	if(m_track)
		return m_track->ReInitSoundInstance();
	return false;
}

void CSfx::setLoop(bool loop)
{
	m_track->setLoop(loop);
}

void CSfx::InitStatic()
{
	printf("initing static audio\n");
	
	SAFE_DEL(m_track);
	SAFE_DEL(m_mpcStream);

	for(int i = 0; i < CTrackStereo::NUM_BUFFER; i++)
	{
		SAFE_DEL(m_buffer[i]);
	}

	m_track			= new CTrackStereo();
	m_mpcStream		= new CMpcStream();

	//char filename[64];
	//sprintf( filename, "SFX/sfx_%02d.mpc", m_nSfx);

	if(!m_mpcStream->Load(m_filename.c_str(), 22050))
	{
		CleanUp();
		return;
	}

	m_channel = m_mpcStream->GetChannel();
	m_rate = /*m_mpcStream->GetSamplingRate();*/22050;
	m_bufferSize = m_mpcStream->GetPCMSize() * m_channel << 1;

	m_buffer[0] = new audio::s8[m_bufferSize];

	m_track->Cleanup();
	m_track->Init( m_channel, m_rate, m_bufferSize);

	m_nStaticSample = m_mpcStream->Decode((short*)m_buffer[0], m_bufferSize/m_channel>>1);
	m_track->Update(0, m_buffer[0], 0, m_nStaticSample * m_channel << 1);

	SAFE_DEL(m_mpcStream);
	m_isStatic = true;

}

void CSfx::PlayStatic()
{
	if(!m_track)
		return;

	if(m_nLoop < -1)
	{ m_track->setLoop(true); }

	m_track->Play();
	m_status = kPlaying;
}

void CSfx::PlayStream()
{
	if(m_filename.length() < 1)
		return;

	SAFE_DEL(m_track);
	SAFE_DEL(m_mpcStream);

	m_track			= new CTrackStereo();
	m_mpcStream		= new CMpcStream();

	if(!m_mpcStream->Load(m_filename.c_str(), 22050))
	{
		CleanUp();
		return;
	}

	m_channel = m_mpcStream->GetChannel();
	m_rate = 22050;//m_mpcStream->GetSamplingRate();
	m_bufferSize = 4 * MPC_DECODER_BUFFER_LENGTH;

	for(int i = 0; i < CTrackStereo::NUM_BUFFER; i++)
	{
		SAFE_DEL(m_buffer[i]);
		m_buffer[i] = new audio::s8[m_bufferSize];
	}

	m_track->Cleanup();
	m_track->Init( m_channel, m_rate, m_bufferSize, (m_nLoop<-1)?true:false);
	m_currentBuffer = 0;

	for(int j = 0; j < CTrackStereo::NUM_BUFFER; j++)
	{
		//int samples = ;
		if(m_mpcStream->Decode((short*)m_buffer[m_currentBuffer], m_bufferSize>>2))
		{
			m_track->Update(0, (audio::s8*)(m_buffer[m_currentBuffer]), j);
			m_currentBuffer = (++m_currentBuffer)%(CTrackStereo::NUM_BUFFER);
		}

		//if(samples < m_buffersize/4)
		//{ break; }
	}

	m_track->Play();
	m_lastUpdateTime = Application::instance()->GetGameTime();

	m_status = kPlaying;
}

void CSfx::RestartStatic()
{
	if(!m_track)
		return;

	m_track->Stop();
	m_track->Update(0, m_buffer[0], 0);
	m_track->Play();
	m_status = kPlaying;
}

void CSfx::RestartStream()
{
	if(!m_track)
		return;

	m_track->Stop();
	m_mpcStream->GoTo(0);

	m_currentBuffer = 0;

	for(int j = 0; j < CTrackStereo::NUM_BUFFER; j++)
	{
		if(m_mpcStream->Decode((short*)m_buffer[m_currentBuffer], m_bufferSize/4))
		{
			m_track->Update(0, (audio::s8*)(m_buffer[m_currentBuffer]), j);
			m_currentBuffer = (++m_currentBuffer)%(CTrackStereo::NUM_BUFFER);
		}
	}

	m_track->Play();
	m_lastUpdateTime = Application::instance()->GetGameTime();

	m_status = kPlaying;
}

int CSfx::GetStatus() const
{
	return m_status;
}
